package trackexplorer.views;

import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import java.util.LinkedList;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.preferences.ConfigurationScope;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;
import org.osgi.service.prefs.BackingStoreException;
import org.osgi.service.prefs.Preferences;

import trackexplorer.Activator;
import trackexplorer.Calibration;
import trackexplorer.editors.MapInput;
import trackexplorer.editors.MapViewer;
import trackexplorer.wizard.AtlasExportWizard;

public class MapView extends ViewPart{
    public static final String ID = "trackexplorer.views.MapView";
    private TableViewer        viewer;
    private Action             openMapAction;
    private Action             doubleClickAction;
    private Preferences        prefs;
    private Action             openAllMapsAction;
    private Action             saveMapAction;
    private Action             removeMapAction;
    private Action             exportAction;
    private Action             saveTBAction;

    public MapView(){
        prefs = new ConfigurationScope().getNode(MapViewer.ID);
    }

    @Override
    public void createPartControl(Composite parent){
        viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL
            | SWT.V_SCROLL);
        viewer.setContentProvider(new ViewContentProvider());
        viewer.setLabelProvider(new ViewLabelProvider());
        viewer.setSorter(new ViewerSorter());
        viewer.setInput(getViewSite());
        viewer.addDropSupport(DND.DROP_COPY | DND.DROP_MOVE,
            new Transfer[]{FileTransfer.getInstance()},
            new DropTargetAdapter(){

                @Override
                public void drop(DropTargetEvent event){
                    Preferences maps = prefs.node("maps");
                    for(String s: (String[])event.data){
                        File file = new File(s);
                        if(file.isDirectory()){
                            File[] files = file.listFiles(new FileFilter(){

                                public boolean accept(File pathname){
                                    return acceptMap(pathname);
                                }
                            });
                            if(files != null && files.length > 0){
                                file = files[0];
                            }
                        }
                        if(acceptMap(file)){
                            maps.put(file.getAbsolutePath(), file.getName());
                        }
                    }
                    try{
                        prefs.flush();
                    }catch(BackingStoreException e){
                        e.printStackTrace();
                    }
                    viewer.refresh();
                }
            });
        makeActions(parent);
        hookContextMenu();
        hookDoubleClickAction();
        contributeToActionBars();
    }

    class ViewContentProvider implements IStructuredContentProvider{
        public void inputChanged(Viewer v, Object oldInput, Object newInput){}

        public void dispose(){}

        public Object[] getElements(Object parent){
            LinkedList<String> ret = new LinkedList<String>();
            try{
                Preferences node = prefs.node("maps");
                for(String key: node.keys()){
                    if(!acceptMap(new File(key))){
                        node.remove(key);
                    }else{
                        ret.add(key);
                    }
                }
                prefs.flush();
            }catch(BackingStoreException e){
                e.printStackTrace();
            }
            return ret.toArray();
        }
    }

    class ViewLabelProvider extends LabelProvider implements
        ITableLabelProvider{
        public String getColumnText(Object obj, int index){
            return getText(obj);
        }

        public Image getColumnImage(Object obj, int index){
            return getImage(obj);
        }

        @Override
        public Image getImage(Object obj){
            return PlatformUI.getWorkbench().getSharedImages().getImage(
                ISharedImages.IMG_OBJ_ELEMENT);
        }
    }

    private void hookContextMenu(){
        MenuManager menuMgr = new MenuManager("#PopupMenu");
        menuMgr.setRemoveAllWhenShown(true);
        menuMgr.addMenuListener(new IMenuListener(){
            public void menuAboutToShow(IMenuManager manager){
                MapView.this.fillContextMenu(manager);
            }
        });
        Menu menu = menuMgr.createContextMenu(viewer.getControl());
        viewer.getControl().setMenu(menu);
        getSite().registerContextMenu(menuMgr, viewer);
    }

    private void contributeToActionBars(){
        IActionBars bars = getViewSite().getActionBars();
        fillLocalToolBar(bars.getToolBarManager());
    }

    private void fillContextMenu(IMenuManager manager){
        manager.add(removeMapAction);
    }

    private void fillLocalToolBar(IToolBarManager manager){
        manager.add(openMapAction);
        manager.add(openAllMapsAction);
        manager.add(saveMapAction);
        manager.add(saveTBAction);
        manager.add(exportAction);
    }

    private void makeActions(final Composite parent){
        openMapAction = new Action(){
            @Override
            public void run(){
                FileDialog dialog = new FileDialog(parent.getShell(), SWT.OPEN);
                dialog.setFilterNames(new String[]{"Map Files",
                    "All Files (*.*)"});
                dialog.setFilterExtensions(new String[]{"*.map;*.tar", "*.*"});
                dialog.setFilterPath(prefs.get("mapdir", "/"));
                String open = dialog.open();
                if(open == null){
                    return;
                }
                prefs.put("mapdir", new File(open).getParent());
                try{
                    openMap(new File(open));
                }catch(Exception e){
                    e.printStackTrace();
                }
                viewer.refresh();
            }
        };
        openMapAction.setText("Open map");
        openMapAction.setToolTipText("Open map");
        openMapAction.setImageDescriptor(PlatformUI.getWorkbench()
            .getSharedImages().getImageDescriptor(ISharedImages.IMG_OBJ_FILE));
        doubleClickAction = new Action(){
            @Override
            public void run(){
                Object obj = ((IStructuredSelection)viewer.getSelection())
                    .getFirstElement();
                File file = new File(obj.toString());
                try{
                    openMap(file);
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        };

        openAllMapsAction = new Action(){
            @Override
            public void run(){
                DirectoryDialog dialog = new DirectoryDialog(parent.getShell(),
                    SWT.OPEN);
                dialog.setFilterPath(prefs.get("mapsdir", "/"));
                final String open = dialog.open();
                if(open == null){
                    return;
                }
                IRunnableWithProgress op = new IRunnableWithProgress(){
                    public void run(IProgressMonitor monitor)
                        throws InvocationTargetException, InterruptedException{
                        File file = new File(open);
                        prefs.put("mapsdir", file.getParent());
                        findMapFiles(file);
                        try{
                            prefs.flush();
                        }catch(BackingStoreException e){
                            e.printStackTrace();
                        }
                    }
                };
                try{
                    new ProgressMonitorDialog(PlatformUI.getWorkbench()
                        .getActiveWorkbenchWindow().getShell()).run(true, true,
                        op);
                }catch(Exception e){
                    e.printStackTrace();
                }
                viewer.refresh();
            }
        };
        openAllMapsAction.setText("Open all maps");
        openAllMapsAction.setToolTipText("Open all maps");
        openAllMapsAction
            .setImageDescriptor(PlatformUI.getWorkbench().getSharedImages()
                .getImageDescriptor(ISharedImages.IMG_OBJ_FOLDER));
        removeMapAction = new Action(){
            @Override
            @SuppressWarnings("unchecked")
            public void run(){
                Preferences tracks = prefs.node("maps");
                IStructuredSelection els = (IStructuredSelection)viewer
                    .getSelection();
                for(Iterator<Object> iterator = els.iterator(); iterator
                    .hasNext();){
                    Object el = iterator.next();
                    viewer.remove(el);
                    tracks.remove(el.toString());
                }
                try{
                    prefs.flush();
                }catch(BackingStoreException e){
                    e.printStackTrace();
                }
            }
        };
        removeMapAction.setText("Remove map");
        removeMapAction.setToolTipText("Remove map from list");
        removeMapAction.setImageDescriptor(PlatformUI.getWorkbench()
            .getSharedImages()
            .getImageDescriptor(ISharedImages.IMG_TOOL_DELETE));
        saveMapAction = new Action(){
            @Override
            public void run(){
                IWorkbenchPage page = PlatformUI.getWorkbench()
                    .getActiveWorkbenchWindow().getActivePage();
                final IEditorPart editor = page.getActiveEditor();
                if(editor instanceof MapViewer){
                    FileDialog dialog = new FileDialog(parent.getShell(),
                        SWT.SAVE);
                    dialog.setFilterNames(new String[]{"jpeg"});
                    dialog.setFilterExtensions(new String[]{"*.jpg"});
                    dialog.setFilterPath(prefs.get("savemapsdir", "/"));
                    final String save = dialog.open();
                    if(save == null){
                        return;
                    }
                    IRunnableWithProgress op = new IRunnableWithProgress(){

                        public void run(IProgressMonitor monitor)
                            throws InvocationTargetException,
                            InterruptedException{
                            File file = new File(save);
                            prefs.put("savemapsdir", file.getParent());

                            MapViewer mv = (MapViewer)editor;
                            ImageLoader loader = new ImageLoader();
                            loader.data = new ImageData[1];
                            loader.data[0] = mv.getImageData();
                            loader.save(save, SWT.IMAGE_JPEG);
                        }
                    };
                    try{
                        new ProgressMonitorDialog(PlatformUI.getWorkbench()
                            .getActiveWorkbenchWindow().getShell()).run(true,
                            true, op);
                    }catch(Exception e){
                        e.printStackTrace();
                    }
                }
                try{
                    prefs.flush();
                }catch(BackingStoreException e){
                    e.printStackTrace();
                }
            }
        };
        saveMapAction.setText("Save map image");
        saveMapAction.setToolTipText("Save map image");
        saveMapAction.setImageDescriptor(Activator.getDefault()
            .getImageRegistry().getDescriptor(Activator.IMG_OZI));

        saveTBAction = new Action(){
            @Override
            public void run(){
                IWorkbenchPage page = PlatformUI.getWorkbench()
                    .getActiveWorkbenchWindow().getActivePage();
                IEditorPart editor = page.getActiveEditor();
                try{
                    if(editor instanceof MapViewer){
                        final MapViewer mv = (MapViewer)editor;
                        DirectoryDialog dialog = new DirectoryDialog(parent
                            .getShell(), SWT.SAVE);
                        dialog.setFilterPath(prefs.get("savemapsdir", "/"));
                        final String save = dialog.open();
                        if(save == null){
                            return;
                        }
                        IRunnableWithProgress op = new IRunnableWithProgress(){

                            public void run(IProgressMonitor monitor)
                                throws InvocationTargetException,
                                InterruptedException{
                                File file = new File(save);
                                prefs
                                    .put("savemapsdir", file.getAbsolutePath());
                                Activator.saveTBmap(file, ((MapInput)mv
                                    .getEditorInput()).getFile(), mv
                                    .getMapImage());
                            }
                        };
                        try{
                            new ProgressMonitorDialog(PlatformUI.getWorkbench()
                                .getActiveWorkbenchWindow().getShell()).run(
                                true, true, op);
                        }catch(Exception e){
                            e.printStackTrace();
                        }
                    }
                    prefs.flush();
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        };
        saveTBAction.setText("Save map image");
        saveTBAction.setToolTipText("Save map image for TB");
        saveTBAction.setImageDescriptor(Activator.getDefault()
            .getImageRegistry().getDescriptor(Activator.IMG_TB));
        exportAction = new Action(){
            @Override
            public void run(){
                AtlasExportWizard wizard = new AtlasExportWizard();
                wizard.init(PlatformUI.getWorkbench(), null);
                WizardDialog dialog = new WizardDialog(PlatformUI
                    .getWorkbench().getActiveWorkbenchWindow().getShell(),
                    wizard);
                dialog.create();
                dialog.open();
            }
        };
        exportAction.setText("Export atlas");
        exportAction.setToolTipText("Export atlas");

        exportAction.setImageDescriptor(Activator.getDefault()
            .getImageRegistry().getDescriptor(Activator.IMG_EXPORT));
    }

    private void openMap(File file) throws Exception{
        if(!acceptMap(file)){
            prefs.flush();
            return;
        }
        IWorkbenchPage page = PlatformUI.getWorkbench()
            .getActiveWorkbenchWindow().getActivePage();
        page.openEditor(new MapInput(file), MapViewer.ID);
        Preferences maps = prefs.node("maps");
        maps.put(file.getAbsolutePath(), file.getName());
        prefs.flush();
    }

    private boolean acceptMap(File file){
        if(!file.exists()){
            return false;
        }
        String name = file.getName().toLowerCase();
        if(name.endsWith(".tar")){
            return true;
        }
        if(!name.endsWith(".map")){
            return false;
        }
        File set = new File(file.getParentFile(), "set");
        if(set.exists() && set.isDirectory()){
            return true;
        }
        final String na = name.substring(0, name.length() - 4);
        File[] files = file.getParentFile().listFiles(new FilenameFilter(){

            public boolean accept(File dir, String name){
                String tmp = name.toLowerCase().substring(0, name.length() - 4);
                boolean q = tmp.equals(na);
                boolean w = Calibration.isGraphics(name);
                return q && w;
            }
        });
        if(files == null || files.length < 1){
            return false;
        }
        return true;
    }

    private void hookDoubleClickAction(){
        viewer.addDoubleClickListener(new IDoubleClickListener(){
            public void doubleClick(DoubleClickEvent event){
                doubleClickAction.run();
            }
        });
    }

    @Override
    public void setFocus(){
        viewer.getControl().setFocus();
    }

    private void findMapFiles(File dir){
        File[] files = dir.listFiles();
        for(File file: files){
            if(acceptMap(file)){
                prefs.node("maps").put(file.getAbsolutePath(), file.getName());
            }
            if(file.isDirectory()){
                findMapFiles(file);
            }
        }
    }
}
